﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Threading;
using Yottamud_2013_v3_Main;
using System.Windows.Controls;
using System.Text.RegularExpressions;
using System.Timers;
using Yottamud_2013_v3_Main.Game_Files;

namespace Yottamud_Project_V3
{
    public class GameDataMonitor
    {
        #region Variables & Declarations

        public event TriggerTimeHandler TriggerTime;
        public delegate void TriggerTimeHandler(string rTime);

        private BackgroundWorker _BGW = new BackgroundWorker();
        private MainWindow _inputGUI;
        private UIUpdater _updater;
        private DateTime _startTime;
        private DateTime _stopTime;
        private Yottamud.Terminal _gameDisplay;
        private List<GameTrigger> Triggers = new List<GameTrigger>();
        private string gameInformation = string.Empty;
        private GameTrigger trigger = new GameTrigger();
        private string tData = "";
        private bool doneParser = false;
        private int parseTimeOut = 0;
        private int triggerIndex = 0;
        private string tRoomName = string.Empty;
        private event CombatManager.CombatHandler OnCombat;
        private CombatManager _combatManager = new CombatManager();
        private int timeOut = 10;
        private bool _isResting = false;
        List<ResourceTypes.NpcType> npcList = new List<ResourceTypes.NpcType>();
        private System.Timers.Timer _resetCombat = new System.Timers.Timer(6000);

        #endregion

        #region Structures

        public struct GameTrigger
        {
            public GameInformation Type;
            public string Start;
            public string Middle;
            public string End;
            public string Data;
            public object MiscData;

        }

        public enum DataType
        {
            Pregame,
            Ingame,
            Debug
        }

        public enum GameInformation
        {
            Stats,
            Exp,
            InRoom,
            AlsoHere,
            MonsterCombatDamage,
            Inventory,
            StatLine,
            Custom,
            Exits,
            Room,
            PlayerCombatDamage,
            Communication,
            CombatOn,
            CombatOff,
            LoginUser,
            LoginPassword,
            Resting,
            Meditating,
            NoEffectSpell,
            NoEffectWeapon,
            GainExperience
        }

        #endregion

        private void Combat_Elapsed(object sender, ElapsedEventArgs e)
        {
            _resetCombat.Enabled = false;

        }

        public void AddToTriggers(GameTrigger iTrigger)
        {
            Triggers.Add(iTrigger);
        }
        /// <summary>
        /// LoadTriggers is used to find specific information in the game data stream.
        /// </summary>
        private void LoadTriggers()
        {
            Triggers.Add(new GameTrigger { Type = GameInformation.NoEffectSpell, Start = "Your spell has no effect in this room!" });
            Triggers.Add(new GameTrigger { Type = GameInformation.Resting, Start = "(Rest", End = "ing)" });
            Triggers.Add(new GameTrigger { Type = GameInformation.CombatOn, Start = "*Combat", End = "Engaged*\r\n" });
            Triggers.Add(new GameTrigger { Type = GameInformation.CombatOff, Start = "*Combat", End = "Off*\r\n" });
            Triggers.Add(new GameTrigger { Type = GameInformation.InRoom, Start = "You notice", End = "here." });
            Triggers.Add(new GameTrigger { Type = GameInformation.Exits, Start = "Obvious exits:", End = "[HP" });
            Triggers.Add(new GameTrigger { Type = GameInformation.AlsoHere, Start = "Also here:", End = "." });
            Triggers.Add(new GameTrigger { Type = GameInformation.Stats, Start = "Name:", End = "[" });
            Triggers.Add(new GameTrigger { Type = GameInformation.StatLine, Start = "[HP=", End = "]:" });
            Triggers.Add(new GameTrigger { Type = GameInformation.Inventory, Start = "You are carrying", End = "%]" });
            Triggers.Add(new GameTrigger { Type = GameInformation.Exp, Start = "Exp:", End = "%]" });
            Triggers.Add(new GameTrigger { Type = GameInformation.MonsterCombatDamage, Start = "The ", Middle = "damage!", End = "\r\n" });
            Triggers.Add(new GameTrigger { Type = GameInformation.GainExperience, Start = "You gain ", End = " experience." });
        }

        public GameDataMonitor(MainWindow inputGUI, Yottamud.Terminal gameDisplay)
        {
            _inputGUI = inputGUI;
            _gameDisplay = gameDisplay;
            _updater = new UIUpdater(_inputGUI);

            LoadTriggers();

            gameDisplay.GameDataHandle += new Yottamud.Terminal.GameDataHandler(ProcessGameData);
            OnCombat += new CombatManager.CombatHandler(_combatManager.ProcessCombat);

        }
        /// Clear out the game data queue collected in the terminal library.
        /// </summary>
        /// <param name="iSize"></param>
        private void ClearGameData(int iSize)
        {
            //char data;

            //for (int i = 0; i < iSize; i++)
            //{
            //    // take data out of the queue and throw it away,
            //    // this data should already be processed at this point.
            //    _gameDisplay.GameData.TryDequeue(out data);
            //}
        }

        private string BuildGameData()
        {
            string gameData = string.Empty;

            gameData = new string(_gameDisplay.GameData, 0, _gameDisplay.CurrentGameDataIndex);
           // gameData = gameData.Replace(MainWindow.CurrentCommand, "").Trim();
            _gameDisplay.CurrentGameDataIndex = 0;

            return gameData;
        }
        /// <summary>
        /// ProcessGameData is the main processor of Majormud data incoming to the user, 
        /// this includes:
        /// Exits, Monsters, Items, Rooms information.
        /// </summary>
        /// <param name="iData"></param>
        public void ProcessGameData(int dataType)
        {
            try
            {
               
                gameInformation = BuildGameData();

                _isResting = false;
               
                doneParser = false;

                triggerIndex = 0;

                tRoomName = string.Empty;

                while (!doneParser)
                {

                    if (gameInformation.Contains("Also here"))
                    {

                    }
                    doneParser = GameDataDecoder.CheckIfDone(gameInformation);

                    if (tRoomName == "")
                    {
                        // See if the room we are in is "known".
                        tRoomName = GameDataDecoder.ExtractRoomName(gameInformation);
                    }

                    bool autoAttack = PlayerManager.PlayerStatusSettings.IsAutoAttackEnabled;


                    if (trigger.Data != null && trigger.Data.Length > 0)
                    {
                        // Do some processing!
                        switch (trigger.Type)
                        {
                            case GameInformation.GainExperience:
                                PlayerManager.PlayerStatusSettings.IsInCombat = false;
                                UInt64 expEarned = GameDataDecoder.DecodeGainExperience(trigger.Data);
                                break;
                            case GameInformation.NoEffectSpell:
                                
                                PlayerManager.PlayerStatusSettings.IsInCombat = false;

                                break;
                            case GameInformation.NoEffectWeapon:
                                PlayerManager.PlayerStatusSettings.IsInCombat = false;

                                break;
                            case GameInformation.Resting:
                                _isResting = true;
                                break;
                            case GameInformation.LoginUser:
                                //_inputGUI.gameDisplay.DispatchMessage(this, trigger.MiscData + "\r\n");
                                //  ClearGameData(gameInformation.Length);
                                break;
                            case GameInformation.LoginPassword:
                                // _inputGUI.gameDisplay.DispatchMessage(this, trigger.MiscData + "\r\n");
                                // ClearGameData(gameInformation.Length);
                                break;
                            case GameInformation.CombatOn:
                                if (!PlayerManager.PlayerStatusSettings.IsInCombat)
                                {
                                    PlayerManager.PlayerStatusSettings.IsInCombat = true;
                                    _updater.UpdateCombatStatus(true);
                                }
                                break;
                            case GameInformation.CombatOff:
                                if (PlayerManager.PlayerStatusSettings.IsInCombat)
                                {
                                    PlayerManager.PlayerStatusSettings.IsInCombat = false;
                                    _updater.UpdateCombatStatus(false);
                                    MainWindow.SelectedDisplay.DispatchMessage(this, "\r\n");
                                }
                                break;
                            case GameInformation.Room:

                                break;
                            case GameInformation.MonsterCombatDamage:

                                _updater.UpdateCombat(trigger.Data);

                                break;

                            case GameInformation.InRoom:

                                GameDataDecoder.DecodeItemsInRoom(trigger.Data);

                                break;
                            case GameInformation.Stats:
                                GameDataDecoder.StatLineType statData = GameDataDecoder.DecodeStats(trigger.Data);

                                _updater.UpdateMaximumHP(statData);

                                break;
                            case GameInformation.StatLine:
                                GameDataDecoder.StatLineType statline = GameDataDecoder.DecodeStatline(trigger.Data);
                                _updater.UpdateCurrentHitpoints(statline);
                                break;
                            case GameInformation.AlsoHere:
                                // Decode who is in the room with us!
                                npcList.Clear();

                                npcList = GameDataDecoder.DecodeNPC(trigger.Data);

                                foreach (ResourceTypes.NpcType npc in npcList)
                                {
                                    _updater.AddToMonsterList(npc.Name, npc.Experience, npc.Number);

                                    OnCombat(npc);
                                }

                                //_BGW.ReportProgress((int)GameInformation.AlsoHere, npcList);
                                break;
                            case GameInformation.Exits:
                                // Decode all possible exits
                                List<string> exitList = GameDataDecoder.DecodeExits(trigger.Data);

                                
                                if (tRoomName.Length > 0)
                                {
                                    ResourceTypes.RoomType roomData = GameDataDecoder.FindKnownRoom(tRoomName, exitList, npcList);

                                    if (roomData != null)
                                    {
                                        _updater.UpdateRoom(roomData.RoomName + ":" + roomData.MapNumber + ":" + roomData.RoomNumber);
                                        
                                        gameInformation = gameInformation.Replace(roomData.RoomName, "");
                                        
                                        // Game issue here patched by yotta
                                        gameInformation = gameInformation.Replace("\r\n=", "[HP=");

                                        if (roomData.ShopID != 0)
                                        {
                                            _updater.DisplayShopItems(roomData.ShopID);
                                        }
                                    }
                                }
                                break;
                            case GameInformation.Exp:
                                // Decode current / needed exp
                                GameDataDecoder.ExperienceType expData = GameDataDecoder.DecodeExperience(trigger.Data);
                                break;
                            case GameInformation.Communication:
                                 GameDataDecoder.CommunicationType cData = (GameDataDecoder.CommunicationType)trigger.MiscData;
                                _updater.UpdateCommunications(cData);
                                break;
                            case GameInformation.Inventory:
                                InventoryManager.InventoryType inventory = InventoryManager.DecodeInventoryItems(trigger.Data);
                                _updater.UpdatePlayerInventory(inventory);
                                break;
                        }
                    }


                    if (triggerIndex == Triggers.Count)
                    {
                        break;
                    }

                    trigger = FindTrigger(ref gameInformation);

                    triggerIndex++;
                }

                // Cleanup stuff.
                _updater.UpdateRestStatus(_isResting);
                if (_isResting)
                {
                    PlayerManager.PlayerStatusSettings.IsResting = true;
                }

            }
            catch { }
        }

        private GameTrigger FindTrigger(ref string gameData)
        {
            GameTrigger foundTriggers = new GameTrigger();

            try
            {
                if (gameData.Contains("gossips") || gameData.Contains("telepaths") || gameData.Contains("gangpaths"))
                {

                    GameDataDecoder.CommunicationType gData = GameDataDecoder.DecodeCommunication(gameData);

                    foundTriggers = new GameTrigger { Type = GameInformation.Communication, Data = gData.Player + " " + gData.ChatDataType.ToString().ToLower() + ": " + gData.Data, MiscData = gData };
                }
                else
                {
                    foreach (GameTrigger trigger in Triggers)
                    {
                        timeOut = 10;
                        if (trigger.Start.Contains("Obvious")&& gameData.Contains("Obvious"))
                        {

                        }
                        if (trigger.Middle != null && trigger.End != null)
                        {
                            if (gameData.Contains(trigger.Start) && gameData.Contains(trigger.End) && gameData.Contains(trigger.Middle))
                            {
                                int tStartIndex = gameData.IndexOf(trigger.Start);

                                int tMiddleIndex = gameData.IndexOf(trigger.Middle, tStartIndex);

                                while (tMiddleIndex <= tStartIndex)
                                {
                                    tMiddleIndex = gameData.IndexOf(trigger.Middle, tStartIndex);

                                    if (timeOut == 0)
                                    {
                                        break;
                                    }

                                    timeOut--;
                                }

                                int tEndIndex = gameData.IndexOf(trigger.End, tMiddleIndex + tStartIndex);

                                while (tEndIndex <= tMiddleIndex)
                                {
                                    tEndIndex = gameData.IndexOf(trigger.End, tMiddleIndex) + trigger.End.Length;

                                    if (timeOut == 0)
                                    {
                                        break;
                                    }

                                    timeOut--;
                                }

                                if (timeOut == 0)
                                {
                                    break;
                                }

                                string fData = gameData.Substring(tStartIndex, tEndIndex - tStartIndex);

                                foundTriggers = new GameTrigger { Type = trigger.Type, Data = fData };
                                break;
                            }
                        }
                        else if (trigger.Middle == null && trigger.End != null)
                        {
                            if (gameData.Contains(trigger.Start) && gameData.Contains(trigger.End))
                            {
                                int tStartIndex = gameData.IndexOf(trigger.Start);

                                int tEndIndex = gameData.IndexOf(trigger.End) + trigger.End.Length;

                                while (tEndIndex < tStartIndex)
                                {
                                    tEndIndex = gameData.IndexOf(trigger.End, tStartIndex) + trigger.End.Length;

                                    if (timeOut == 0)
                                    {
                                        break;
                                    }

                                    timeOut--;

                                }

                                if (timeOut == 0)
                                {
                                    break;
                                }

                                string fData = gameData.Substring(tStartIndex, tEndIndex - tStartIndex);

                                foundTriggers = new GameTrigger { Type = trigger.Type, Data = fData };
                                break;
                            }
                        }
                        else if (trigger.Middle == null && trigger.End == null)
                        {
                            if (gameData.Contains(trigger.Start))
                            {
                                int tStartIndex = gameData.IndexOf(trigger.Start);

                                string fData = gameData.Substring(tStartIndex, gameData.Length - tStartIndex);

                                foundTriggers = new GameTrigger { Type = trigger.Type, Data = fData, MiscData = trigger.MiscData };

                                break;
                            }
                        }
                    }


               }

                if (foundTriggers.Data != null && foundTriggers.Data != "")
                {
                    gameData = gameData.Replace(foundTriggers.Data, "");
                }
            }
            catch { }
            return foundTriggers;

        }
    }
}
